package com.kehutong.webchat.service;

import com.kehutong.common.DailyUtil;
import com.kehutong.common.DoveClient;
import com.kehutong.common.entity.Root;
import com.kehutong.webchat.entity.MemberMap;
import com.kehutong.webchat.entity.WeChatSetting;
import com.kehutong.webchat.enums.WeChatType;
import com.kehutong.webchat.util.AESUtils;
import com.kehutong.webchat.util.WechatHttpClient;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.http.ReqMapping;
import org.coraframework.mvc.http.result.Result;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.annotation.Column;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.execute.entity.FindOne;
import org.coraframework.util.IDGenerate;
import org.coraframework.util.Objects;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@WebService("/kht-bin/wechat")
public class WeChatService {

    static final Logger logger = LoggerFactory.getLogger(WeChatService.class);

    @Inject
    private JdbcSession jdbcSession;

    @Inject
    private WechatHttpClient httpClient;
    @Inject
    private JdbcSession SignJDBCSession;

    @Inject
    private DoveClient doveClient;

    @ReqMapping("/getUserInfo")
    public Object getUserInfo(String appId, String code, String iv, String encryptedData) throws Exception {
        WeChatSetting setting = getWechatSetting(appId);
        if (Objects.isNull(setting)) {
            return Result.error(0, "未知应用");
        }

        //获取小程序相关接口
        JSONObject json = getOpenId(setting, code);
        if (Objects.isEmpty(json)) {
            return Result.error(0, "openId is null");// 用户不能为空
        }

        String unionId = json.getString("unionid");
        String openId = json.getString("openid");
        if (Objects.isEmpty(unionId)) {
            return Result.error(0, "unionId is null");//unionId不能为空
        }



        MemberMap member = getMemberMapByMn(unionId, openId);


        if (Objects.nonNull(member)) {
            if (Objects.isEmpty(member.getUnionid()) || Objects.isEmpty(member.getMn_open_id())) {
                member.setUnionid(unionId);
                member.setMn_open_id(openId);
                member.setMn_app_id(appId);
            }
            if(Objects.isEmpty(member.getKhtId())){
                if (Objects.isEmpty(encryptedData)) {
                    return Result.error(10001, "用户信息不能为空");
                }
                //首次小程勋绑定
                JSONObject mobileJson = AESUtils.decrypt(encryptedData, json.getString("session_key"), iv);
                //解密获取小程序首次登录传递的手机号
                String mobile = (Objects.nonEmpty(mobileJson)) ? mobileJson.getString("phoneNumber") : "";
                String khtId = IDGenerate.nextId();
                String accountNo = saveOrUpdateAccount(khtId, mobile);
                member.setKhtId(accountNo);

            }
            jdbcSession.updateById(member);
            return loginResult(member, null, false);
        }

        if (Objects.isEmpty(encryptedData)) {
            return Result.error(10001, "用户信息不能为空");
        }
        //首次小程勋绑定
        JSONObject mobileJson = AESUtils.decrypt(encryptedData, json.getString("session_key"), iv);
        //解密获取小程序首次登录传递的手机号
        String mobile = (Objects.nonEmpty(mobileJson)) ? mobileJson.getString("phoneNumber") : "";
        String khtId = IDGenerate.nextId();
        //新增或更新账号
        String accountNo = saveOrUpdateAccount(khtId, mobile);
        if (Objects.isEmpty(accountNo)) {
            return Result.error(0, "新增或更新住户账号信息失败.");
        }
        member = createMember(unionId, openId, appId, accountNo);
        return loginResult(member, mobileJson, true);
    }

    /**
     * 获取公众号用户信息
     */
    @ReqMapping("/getMpUserInfo")
    public Object getMpUserInfo(String appId, String code) throws Exception {
        WeChatSetting setting = getWechatSetting(appId);
        if (Objects.isNull(setting)) {
            return Result.error(0, "未知应用");
        }


        //获取公众号相关接口
        JSONObject json = getMpOpenId(setting, code);
        if (Objects.isEmpty(json)) {
            return Result.error(0, "openId is null");// 用户不能为空
        }

        String openId = json.getString("openid");
        JSONObject mpUserInfo = getMpUserInfo(setting, openId);
        String errorCode = mpUserInfo.getString("errcode");
        if (Objects.nonEmpty(errorCode) && !Objects.equal(errorCode, "0")) {
            return Result.error(0, mpUserInfo.getString("errmsg"));
        }
        String unionId = mpUserInfo.getString("unionid");

        if (Objects.isEmpty(unionId)) {
            return Result.error(0, "unionId is null");//unionId不能为空
        }

        MemberMap member = getMemberMapByMp(unionId, openId);
        if (Objects.nonNull(member)) {
            if (Objects.isEmpty(member.getUnionid()) || Objects.isEmpty(member.getMp_open_id())) {
                member.setUnionid(unionId);
                member.setMp_open_id(openId);
                member.setMp_app_id(appId);
                member.setMp_subscribe(true);
                jdbcSession.updateById(member);
            }
        } else {
            member = Pool.newInstance(MemberMap.class, unionId);
            member.setUnionid(unionId);
            member.setMp_open_id(openId);
            member.setMp_app_id(appId);
            member.setMp_subscribe(true);
            jdbcSession.insert(member);
        }
        return Result.success(member);
    }


    public WeChatSetting getWechatSetting(String appId) {
        WeChatSetting setting = Pool.get(WeChatSetting.class, appId);
        if (setting == null || setting.isDeleted()) {
            return null;
        }

        return setting;
    }

    @ReqMapping("/getWechatSetting")
    public Object getWechatSetting(JSONObject jsonObject) throws Exception {
        //首先查看公司有没有单独的小程序及公众号配置
        List<WeChatSetting> weChatSettingList = jdbcSession.findArray(WeChatSetting.class)
                .eq(jsonObject, "appId", "type", "companyNo")
                .eq("deleted", false)
                .exe();
        //公司没有单独的小程序及公众号配置,则获取系统默认的小程序及公司配置
        if (Objects.isEmpty(weChatSettingList)) {
            weChatSettingList = jdbcSession.findArray(WeChatSetting.class)
                    .eq(jsonObject, "appId", "type")
                    .eq("companyNo", Root.ROOT_ID)
                    .eq("deleted", false)
                    .exe();
        }
        return Result.success(weChatSettingList);
    }

    @ReqMapping("/getPaidUnionId")
    public Object getPaidUnionId(JSONObject jsonObject) throws Exception {
        String appId = jsonObject.getString("appId");
        String openid = jsonObject.getString("openid");
        String transactionId = jsonObject.getString("transactionId");
        if (Objects.isEmpty(appId)) {
            return Result.error(0, "appId不能为空.");
        }

        if (Objects.isEmpty(openid)) {
            return Result.error(0, "openid不能为空.");
        }

        if (Objects.isEmpty(transactionId)) {
            return Result.error(0, "transactionId不能为空.");
        }
        WeChatSetting setting = jdbcSession.findOne(WeChatSetting.class)
                .eq(jsonObject, "appId").exe();

        if (Objects.isNull(setting)) {
            return Result.error(0, "微信应用没有相关配置.");
        }
        JSONObject resultJson = httpClient.getByAccessToken("/wxa/getpaidunionid", setting, http -> {
/*			JSONObject body = new JSONObject();
			body.put("openid", openid);
			body.put("transaction_id", transactionId);
			http.setBody(body.toString());*/
            http.addParam("openid", openid);
            http.addParam("transaction_id", transactionId);
/*			http.addParam("mch_id", jsonObject.getString("mch_id"));
			http.addParam("out_trade_no", jsonObject.getString("out_trade_no"));*/
        });
        logger.info("##############################getPaidUnionId:{}", resultJson.toJSONString());
        String errorCode = resultJson.getString("errcode");
        if (Objects.nonEmpty(errorCode) && !Objects.equal(errorCode, "0")) {
            return Result.error(0, resultJson.getString("errmsg"));
        }
        return Result.success(resultJson.getString("unionid"));
    }


    @ReqMapping("/getMemberMap")
    public Object getMemberMap(JSONObject jsonObject) throws Exception {
        String unionId = jsonObject.getString("unionid");
        String khtId = jsonObject.getString("khtId");
        String mn_app_id = jsonObject.getString("mn_app_id");
        String companyNo = jsonObject.getString("companyNo");

        //根据unionid获取用户映射关系
        if (Objects.nonEmpty(unionId)) {
            return jdbcSession.findOne(MemberMap.class)
                    .eq(jsonObject, "unionid").exe();
        }
        //根据账号编号与小程序编号获取用户映射关系
        if (Objects.nonEmpty(khtId) && Objects.nonEmpty(mn_app_id)) {
            return jdbcSession.findOne(MemberMap.class)
                    .eq(jsonObject, "khtId", "mn_app_id").exe();
        }
        //根据账号编号与公司编号获取用户映射关系
        if (Objects.nonEmpty(khtId) && Objects.nonEmpty(companyNo)) {
            //首先查看公司有没有单独的小程序及公众号配置
            List<WeChatSetting> weChatSettingList = jdbcSession.findArray(WeChatSetting.class)
                    .eq("companyNo", companyNo)
                    .eq("deleted", false)
                    .exe();
            //公司没有单独的小程序及公众号配置,则获取系统默认的小程序及公司配置
            if (Objects.isEmpty(weChatSettingList)) {
                weChatSettingList = jdbcSession.findArray(WeChatSetting.class)
                        .eq("companyNo", Root.ROOT_ID)
                        .eq("deleted", false)
                        .exe();
            }

            if (Objects.isEmpty(weChatSettingList)) {
                return Result.success();
            }
            for (WeChatSetting weChatSetting : weChatSettingList) {
                if (Objects.equal(weChatSetting.getType().getId(), WeChatType.gongzonghao.getId())) {
                    jsonObject.put("mp_app_id", weChatSetting.getAppId());
                } else if (Objects.equal(weChatSetting.getType().getId(), WeChatType.xiaochengxu.getId())) {
                    jsonObject.put("mn_app_id", weChatSetting.getAppId());
                }
            }
            FindOne<MemberMap> find = jdbcSession.findOne(MemberMap.class)
                    .eq(jsonObject, "khtId");
            find.markBegin();
            if (Objects.nonEmpty(jsonObject.getString("mp_app_id"))) {
                find.eq("mp_app_id", jsonObject.getString("mp_app_id")).or();
            }
            if (Objects.nonEmpty(jsonObject.getString("mn_app_id"))) {
                find.eq("mn_app_id", jsonObject.getString("mn_app_id")).or();
            }
            find.markEnd();
            return find.exe();
        }
        return Result.success();
    }

    public MemberMap getMemberByOpenId(String openId) {
        MemberMap member = jdbcSession.findOne(MemberMap.class)
                .eq("mn_open_id", openId)
                .exe();
        if (member == null || member.isDeleted()) {
            return null;
        }

        return member;
    }

    public MemberMap getMemberMapByMn(String unionId, String openId) {
        MemberMap member = jdbcSession.findOne(MemberMap.class)
                .markBegin()
                .eq("unionid", unionId).or()
                .eq("mn_open_id", openId).or()
                .markEnd().exe();
        if (member == null || member.isDeleted()) {
            return null;
        }

        return member;
    }

    public MemberMap getMemberMapByMp(String unionId, String openId) {
        MemberMap member = jdbcSession.findOne(MemberMap.class)
                .markBegin()
                .eq("unionid", unionId).or()
                .eq("mp_open_id", openId).or()
                .markEnd().exe();
        if (member == null || member.isDeleted()) {
            return null;
        }
        return member;
    }

    public MemberMap getMemberByMpOpenId(String openId) {
        MemberMap member = jdbcSession.findOne(MemberMap.class)
                .eq("mp_open_id", openId)
                .exe();
        if (member == null || member.isDeleted()) {
            return null;
        }

        return member;
    }

    private JSONObject getOpenId(WeChatSetting setting, String code) throws Exception {
        if (Objects.isEmpty(code)) {
            return null;
        }

        JSONObject jsonObject = httpClient.get("/sns/jscode2session", (http) -> {
            http.addParam("grant_type", "authorization_code");
            http.addParam("appid", setting.getAppId());
            http.addParam("secret", setting.getAppSecret());
            http.addParam("js_code", code);
        });

        return jsonObject;
    }


    private JSONObject getMpOpenId(WeChatSetting setting, String code) throws Exception {
        if (Objects.isEmpty(code)) {
            return null;
        }

        JSONObject jsonObject = httpClient.get("/sns/oauth2/access_token", (http) -> {
            http.addParam("appid", setting.getAppId());
            http.addParam("secret", setting.getAppSecret());
            http.addParam("code", code);
            http.addParam("grant_type", "authorization_code");
        });

        return jsonObject;
    }


    // ===============================================================================
    // 微信公众号相关
    // ===============================================================================
    public JSONObject getMpUserInfo(WeChatSetting setting, String openId) throws Exception {
//		String openId=json.getString("openid");
//		String unionId=json.getString("unionid");
//		String sessionKey=json.getString("session_key");
//		String nickName=json.getString("nickName");
//		String avatar=json.getString("avatar");
        // TODO: 因未接入取消关注回调事件，故下方代码注释，用于取消关注公众号时给予提示
        // if (Objects.nonNull(contact) && Objects.nonEmpty(contact.getUnionid())) {
        // 	return contact;
        // }
        return httpClient.getByAccessToken("/cgi-bin/user/info", setting, http -> {
            http.addParam("lang", "zh_CN");
            http.addParam("openId", openId);
        });
    }

    public String getAccessToken(String appId) throws Exception {
        WeChatSetting setting = Pool.get(WeChatSetting.class, appId);
        if (Objects.isNull(setting)) {
            throw new NullPointerException("app not exist, appId=" + appId);
        }

        return getAccessToken(setting);
    }

    public String getAccessToken(WeChatSetting setting) throws Exception {
        if (DailyUtil.isValidTime(setting.getAccessTokenExpiredTime())) {
            return setting.getAccessToken();
        }

        JSONObject jsonObject = httpClient.post("/cgi-bin/token", http -> {
            http.addParam("grant_type", "client_credential");
            http.addParam("appid", setting.getAppId());
            http.addParam("secret", setting.getAppSecret());
        });
        if (logger.isInfoEnabled()) {
            logger.info("WeChatService.getAccessToken:获取微信配置类型{}的accessToken:{}",setting.getType().getDesc(),jsonObject.toString());
        }
        String errorCode = jsonObject.getString("errcode");
        if (Objects.nonEmpty(errorCode) && !Objects.equal(errorCode, "0")) {
            logger.error("WeChatService.getAccessToken:获取微信配置类型{}的accessToken:错误{}",setting.getType().getDesc(),jsonObject.toString());
        }else{
            setting.setAccessToken(jsonObject.getString("access_token"));
            setting.setAccessTokenExpiredTime(LocalDateTime.now().plusSeconds(jsonObject.getLongValue("expires_in")-600));
            jdbcSession.updateById(setting);
        }
        return setting.getAccessToken();
    }

    private JSONObject loginResult(MemberMap member, JSONObject json, boolean created) {
        JSONObject rs = new JSONObject();
        rs.put("uuid", member.getKhtId());
        if (Objects.nonEmpty(json)) {
            rs.put("mobile", json.getString("phoneNumber"));
        }
        rs.put("created", created);
        return rs;
    }

//	private MemberMap createMember(String unionid, String openId) {
//		MemberMap item = Pool.newInstance(MemberMap.class, unionid);
//		item.setMn_open_id(openId);
//		item.setUnionid(unionid);
//		item.setKhtId(IDGenerate.nextId());
//		
//		jdbcSession.insert(item);
//		return item;
//	}

    private MemberMap createMember(String unionid, String openId, String appId, String khtId) {
        MemberMap item = Pool.newInstance(MemberMap.class, unionid);
        item.setMn_open_id(openId);
        item.setMn_app_id(appId);
        item.setUnionid(unionid);
        item.setKhtId(khtId);

        jdbcSession.insert(item);
        return item;
    }

    @ReqMapping("/saveWxMember")
    public Object saveOrUpdateWxMember(String unionid,String khtId,String companyNo,String companyName,String externalUserId) {
        MemberMap member = jdbcSession.findOne(MemberMap.class)
                .markBegin()
                .eq("unionid", unionid)
                .markEnd().exe();
        if(Objects.isNull(member)){
            List<String> companyNos=new ArrayList<>();
            companyNos.add(companyNo);
            List<String> companyNames=new ArrayList<>();
            companyNames.add(companyName);
            List<String> externalUserIds=new ArrayList<>();
            externalUserIds.add(externalUserId);
            MemberMap item = Pool.newInstance(MemberMap.class, unionid);
            item.setUnionid(unionid);
            item.setKhtId(khtId);
            item.setCompanyNos(companyNos);
            item.setCompanyNames(companyNames);
            item.setExternalUserIds(externalUserIds);
            jdbcSession.insert(item);
        }else{
            Set<String> companyNos = new HashSet<>();
            //获取账户已经绑定的公司编号集合
            if (Objects.nonEmpty(member.getCompanyNos())) {
                companyNos = new HashSet<>(member.getCompanyNos());
            }
            Set<String> companyNames=new HashSet<>();
            if (Objects.nonEmpty(member.getCompanyNames())) {
                companyNames = new HashSet<>(member.getCompanyNames());
            }

            Set<String> externalUserIds=new HashSet<>();
            if (Objects.nonEmpty(member.getCompanyNos())) {
                externalUserIds = new HashSet<>(member.getExternalUserIds());
            }

            companyNos.addAll(new HashSet<>(companyNos));
            member.setCompanyNos(new ArrayList<>(companyNos));

            companyNames.addAll(new HashSet<>(companyNames));
            member.setCompanyNames(new ArrayList<>(companyNames));

            externalUserIds.addAll(new HashSet<>(externalUserIds));
            member.setExternalUserIds(new ArrayList<>(externalUserIds));
            jdbcSession.updateById(member);
        }
        return Result.success();
    }

    /**
     * 新增或更新账户信息
     *
     * @param id     客户通编号
     * @param mobile 手机号
     * @throws Exception 异常
     */
    private String saveOrUpdateAccount(String id, String mobile) throws Exception {
        String accountNo = "";
        JSONObject saveOrUpdateAccountResult = doveClient.post("/auth/account/service/saveOrUpdateAccount", (http) -> {
            JSONObject body = new JSONObject();
            body.put("id", id);
            body.put("mobile", mobile);
            http.setBody(body.toJSONString());
        });
        if (saveOrUpdateAccountResult.getIntValue("code") != 200) {
            logger.error("WeChatService.saveOrUpdateAccountResult 新增或更新账户信息失败.");
        }
        JSONObject accountJsonObject = saveOrUpdateAccountResult.getJSONObject("data");
        if (Objects.nonEmpty(accountJsonObject)) {
            accountNo = accountJsonObject.getString("id");
        }
        return accountNo;
    }

}
